package top.hmtools.wxmp.core.httpclient;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;

import top.hmtools.wxmp.core.httpclient.responseHandler.EResponseHandler;
import top.hmtools.wxmp.core.tools.Bean2MapTools;

public class HmHttpClientTools {

	private static final Logger logger = LoggerFactory.getLogger(HmHttpClientTools.class);
	
	/**
	 * 以域名为key，httphost为value，维护全局路由信息字典
	 */
	public static HashMap<String, HttpHost> HTTP_HOST_ROUTERS;
	
	/**
	 * 以域名为key，HttpClientContext为value，维护全局http请求上下文信息字典
	 */
	public static HashMap<String, HttpClientContext> HTTP_CLIENT_CONTEXT;
	
	static{
		//初始化路由字典
		if(HTTP_HOST_ROUTERS == null){
			synchronized (HmHttpClientTools.class) {
				if(HTTP_HOST_ROUTERS == null){
					HTTP_HOST_ROUTERS = new HashMap<>();
				}
			}
		}
		
		//初始化上下文字典
		if(HTTP_CLIENT_CONTEXT == null){
			synchronized (HmHttpClientTools.class) {
				if(HTTP_CLIENT_CONTEXT == null){
					HTTP_CLIENT_CONTEXT = new HashMap<>();
				}
			}
		}
	}
	
	/**
	 * 发送http post请求 <br>
	 * 请求参数体：json string <br>
	 * 响应body体：json string <br>
	 * @param url
	 * @param params
	 * @param clazz
	 * @return
	 */
	public static <T>T httpPostReqJsonRespJson(String url, Object params,Class<T> clazz){
		return httpPostReqJsonRespJson(url, params, null, clazz);
	}
	
	/**
	 * 发送http post请求 <br>
	 * 请求参数体：json string <br>
	 * 响应body体：json string <br>
	 * @param url
	 * @param params
	 * @param charset
	 * @param clazz
	 * @return
	 */
	public static <T>T httpPostReqJsonRespJson(String url, Object params,String charset,Class<T> clazz){
		String resultString = httpPostReqJsonRespString(url, params, charset);
		if(resultString == null || resultString.trim().length() < 1){
			return null;
		}else{
			return JSON.parseObject(resultString, clazz);
		}
	}
	
	/**
	 * 发送http post请求 <br>
	 * 请求参数体：json string <br>
	 * 响应body体：string <br>
	 * @param url
	 * @param params
	 * @return
	 */
	public static String httpPostReqJsonRespString(String url, Object params,String charset){
		HttpPost httpPost = new HttpPost(url);
		if(params != null){
			HttpEntity httpEntity = buildJsonEntity(params, charset);
			httpPost.setEntity(httpEntity);
		}
		if (logger.isDebugEnabled()) {
			logger.debug("向服务侧发起http post请求的完整URL是：{}", url);
		}
		return executeHttpRequest(httpPost, EResponseHandler.StringResponse);
	}
	
	/**
	 * 发送http post请求 <br>
	 * 支持文件上传，需自定义参数组装，使用本方法前，建议先调用本类的 top.hmtools.wxmp.core.httpclient.HmHttpClientTools.buildMultipartEntity(Object) <br>
	 * 请求参数体：multi-form <br>
	 * 响应body体：string <br>
	 * @param url
	 * @param multipartEntityBuilder
	 * @param clazz
	 * @return
	 */
	public static <T>T httpPostReqMultiFormRespJson(String url, MultipartEntityBuilder multipartEntityBuilder,Class<T> clazz){
		String resultString = httpPostReqMultiFormRespString(url, multipartEntityBuilder);
		if(resultString == null || resultString.trim().length()<1){
			return null;
		}else{
			return JSON.parseObject(resultString,clazz);
		}
	}
	
	/**
	 * 发送http post请求 <br>
	 * 支持文件上传，需自定义参数组装，使用本方法前，建议先调用本类的 top.hmtools.wxmp.core.httpclient.HmHttpClientTools.buildMultipartEntity(Object) <br>
	 * 请求参数体：multi-form <br>
	 * 响应body体：string <br>
	 * @param url
	 * @param multipartEntityBuilder
	 * @return
	 */
	public static String httpPostReqMultiFormRespString(String url, MultipartEntityBuilder multipartEntityBuilder){
		HttpPost httpPost = new HttpPost(url);
		if(multipartEntityBuilder != null){
			httpPost.setEntity(multipartEntityBuilder.build());
		}
		if (logger.isDebugEnabled()) {
			logger.debug("向服务侧发起http post请求的完整URL是：{}", url);
		}
		return executeHttpRequest(httpPost, EResponseHandler.StringResponse);
	}
	
	/**
	 * 发送http post请求 <br>
	 * 如果要用此方法上传文件，其在params中属性数据类型必须为 java.io.File <br>
	 * 请求参数体：form <br>
	 * 响应body体：string <br>
	 * @param url
	 * @param params
	 * @param clazz
	 * @return
	 */
	public static <T>T httpPostReqFormRespJson(String url, Object params,Class<T> clazz){
		String resultString = httpPostReqFormRespString(url, params);
		if(resultString == null || resultString.trim().length()<1){
			return null;
		}else{
			return JSON.parseObject(resultString, clazz);
		}
	}
	
	/**
	 * 发送http post请求 <br>
	 * 如果要用此方法上传文件，其在params中属性数据类型必须为 java.io.File <br>
	 * 请求参数体：form <br>
	 * 响应body体：string <br>
	 * @param url
	 * @param params
	 * @return
	 */
	public static String httpPostReqFormRespString(String url, Object params){
		HttpPost httpPost = new HttpPost(url);
		if(params!=null){
			HttpEntity httpEntity = buildMultipartEntity(params).build();
			httpPost.setEntity(httpEntity);
		}
		if (logger.isDebugEnabled()) {
			logger.debug("向服务侧发起http post请求的完整URL是：{}", url);
		}
		return executeHttpRequest(httpPost, EResponseHandler.StringResponse);
	}
	
	/**
	 * 发送http get请求 <br>
	 * 请求参数体：URL parameter <br>
	 * 响应body体：inputstream <br>
	 * @param url
	 * @param params
	 * @return
	 */
	public static InputStream httpGetReqParamRespInputStream(String url, Object params){
		URI uri = buildURI(url, params);
		if (logger.isDebugEnabled()) {
			logger.debug("向服务侧发起http get请求的完整URL是：{}", uri.toString());
		}
		HttpGet httpGet = new HttpGet(uri);

		return executeHttpRequest(httpGet, EResponseHandler.InputStreamResponse);
	}
	
	/**
	 * 发送http get请求 <br>
	 * 请求参数体：URL parameter <br>
	 * 响应body体：string <br>
	 * @param url
	 * @param params
	 * @return
	 */
	public static String httpGetReqParamRespString(String url, Object params){
		URI uri = buildURI(url, params);
		if (logger.isDebugEnabled()) {
			logger.debug("向服务侧发起http get请求的完整URL是：{}", uri.toString());
		}
		HttpGet httpGet = new HttpGet(uri);

		return executeHttpRequest(httpGet, EResponseHandler.StringResponse);
	}

	/**
	 * 发送http get请求 <br>
	 * 请求参数体：URL parameter <br>
	 * 响应body体：json <br>
	 * 
	 * @param url
	 * @param params
	 * @param clazz
	 * @return
	 */
	public static <T> T httpGetReqParamRespJson(String url, Object params, Class<T> clazz) {
		String httpResponseContentString = httpGetReqParamRespString(url,params);
		return JSON.parseObject(httpResponseContentString, clazz);
	}

	/**
	 * 生成带请求参数的URI
	 * 
	 * @param url
	 * @param params
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static URI buildURI(String url, Object params) {
		URIBuilder uriBuilder;
		try {
			uriBuilder = new URIBuilder(url);

			// 组装请求参数
			if(params!=null){
				//初始化一个存储 http request param 的 tree map
				Map<String, Object> paramMap = new TreeMap<>();
				
				//给tree map填充数据
				if (params instanceof Map) {
					paramMap.putAll((Map<? extends String, ? extends Object>) params);
				} else {
					Map<String, Object> paramMapTmp = Bean2MapTools.bean2MapPlus(params);
					paramMap.putAll(paramMapTmp);
				}
				
				//遍历tree map，将数据转换成 http request URI param
				Iterator<Entry<String, Object>> iterator = paramMap.entrySet().iterator();
				while (iterator.hasNext()) {
					Entry<String, Object> paramItemTmp = iterator.next();
					uriBuilder.addParameter(paramItemTmp.getKey(), paramItemTmp.getValue().toString());
				}
			}

			// 组装反馈结果
			URI uri = uriBuilder.build();
			return uri;
		} catch (URISyntaxException | IllegalAccessException e) {
			throw new RuntimeException("生成带请求参数的URI异常：", e);
		}
	}
	
	/**
	 * 获取字节数组形式的http request body
	 * @param params
	 * @param charset
	 * @return
	 */
	public static HttpEntity buildJsonEntity(Object params,String charset){
		if(params == null){
			return null;
		}
		
		if(charset == null || charset.trim().length()<1){
			charset = "UTF-8";
		}
		
		String paramsJsonString = JSON.toJSONString(params);
		if(logger.isDebugEnabled()){
			logger.debug("构建的json字符串request body原文是：{}",paramsJsonString);
		}
		try {
			return new ByteArrayEntity(paramsJsonString.getBytes(charset), ContentType.APPLICATION_JSON);
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 生成 http post form requestbody ，含文件上传
	 * <br>注意：
	 * <br>如果含有inputstream、byte[]数据类型的类属性，须在执行完本方法后，再执行 org.apache.http.entity.mime.MultipartEntityBuilder.addBinaryBody(String, byte[], ContentType, String) 添加。
	 * @param params
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static MultipartEntityBuilder buildMultipartEntity(Object params){
		if(params == null){
			return null;
		}
		
		//组装请求参数体 requestBody
		try {
			//将Javabean 转为 map
			Map<String, Object> paramMap = new TreeMap<>();
			if (params instanceof Map) {
				paramMap.putAll((Map<? extends String, ? extends Object>) params);
			} else {
				Map<String, Object> paramMapTmp = Bean2MapTools.bean2MapPlus(params);
				paramMap.putAll(paramMapTmp);
			}
			
			//遍历处理条目
			MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
			Iterator<Entry<String, Object>> iterator = paramMap.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<String, Object> paramItemTmp = iterator.next();
				String key = paramItemTmp.getKey();
				Object value = paramItemTmp.getValue();
				if(value == null){
					continue;
				}
				
				if(value instanceof File){
					//如果参数是文件类型
					FileBody fileBody = new FileBody((File)value);
					multipartEntityBuilder.addPart(key, fileBody);
				}else if(value instanceof byte[]){
					//如果是字节数组  FIXME 如何传入并获取该数据流的 文件类型、文件名称？？
//					ByteArrayBody byteArrayBody = new ByteArrayBody((byte[]) value, ContentType.IMAGE_JPEG,System.currentTimeMillis()+".jpeg");
//					multipartEntityBuilder.addPart(key, byteArrayBody);
					logger.warn("变量名为：{}的数据类型为byte数组，为了更好的使用，须在执行完本方法后获取的返回数据手动添加，参照：org.apache.http.entity.mime.MultipartEntityBuilder.addBinaryBody(String, byte[], ContentType, String)");
				}else if(value instanceof InputStream){
					//如果是输入流  FIXME 如何传入并获取该数据流的 文件类型、文件名称？？
//					InputStreamBody inputStreamBody = new InputStreamBody((InputStream)value, System.currentTimeMillis()+".jpeg");
//					multipartEntityBuilder.addPart(key, inputStreamBody);
					logger.warn("变量名为：{}的数据类型为InputStream，为了更好的使用，须在执行完本方法后获取的返回数据手动添加，参照：org.apache.http.entity.mime.MultipartEntityBuilder.addBinaryBody(String, InputStream, ContentType, String)");
				}else{
					//其它数据类型
					String valueStr = String.valueOf(value).trim();
					multipartEntityBuilder.addPart(key, new StringBody(valueStr, ContentType.TEXT_PLAIN));
				}
			}
			
			return multipartEntityBuilder;
		} catch (IllegalAccessException e) {
			throw new RuntimeException("生成form request body参数异常：", e);
		}
	}
	
	@SuppressWarnings("unchecked")
	public static <T> T executeHttpRequest(HttpRequestBase httpRequest, EResponseHandler eResponseHandler) {
		return (T) executeHttpRequest(httpRequest, eResponseHandler.getResponseHandler());
	}

	/**
	 * 执行http请求
	 * 
	 * @param httpRequest
	 * @param responseHandler
	 * @return
	 */
	public static <T> T executeHttpRequest(HttpRequestBase httpRequest, ResponseHandler<T> responseHandler) {
		CloseableHttpClient httpClient = HmHttpClientFactoryHandle.getPoolingHttpClient();

		// 获取路由 全局维护
		URI uri = httpRequest.getURI();
		HttpHost httpHost = getHttpHost(uri);
		
		// 请求上下文 全局维护
		HttpClientContext httpClientContext = getHttpClientContext(uri);

		try {
			return httpClient.execute(httpHost, httpRequest, responseHandler, httpClientContext);
		} catch (IOException e) {
			logger.error("发送http请求异常：",e);
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 获取http请求上下文
	 * @param uri
	 * @return
	 */
	public static HttpClientContext getHttpClientContext(URI uri){
		if(uri == null){
			return null;
		}
		
		String host = uri.getHost();
		int port = uri.getPort();
		String scheme = uri.getScheme();
		
		//尝试从字典中获取
		String key = scheme+"-"+host+"-"+port;
		HttpClientContext result = HTTP_CLIENT_CONTEXT.get(key);
		
		if(result != null){
			//有，则直接返回
			return result;
		}else{
			//没有，则生成一个，并写入字典
			result = HttpClientContext.create();
			HTTP_CLIENT_CONTEXT.put(key, result);
			return result;
		}
	}
	
	/**
	 * 获取路由？？
	 * @param uri
	 * @return
	 */
	public static HttpHost getHttpHost(URI uri){
		if(uri == null){
			return null;
		}
		
		String host = uri.getHost();
		int port = uri.getPort();
		String scheme = uri.getScheme();
		
		//尝试从字典中获取
		String key = scheme+"-"+host+"-"+port;
		HttpHost result = HTTP_HOST_ROUTERS.get(key);
		
		if(result != null){
			//有，则直接返回
			return result;
		}else{
			//没有，则生成一个，并写入字典
			if (port > 0) {
				result = new HttpHost(host, port);
			} else {
				if(scheme.toUpperCase().equalsIgnoreCase("HTTPS")){
					result = new HttpHost(host,443,scheme);
				}else if(scheme.toUpperCase().equalsIgnoreCase("HTTP")){
					result = new HttpHost(host,80,scheme);
				}else{
					result = new HttpHost(host);
				}
			}
			HTTP_HOST_ROUTERS.put(key, result);
			return result;
		}
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
}
